1 module tools.copyresources; 2 import commons; 3 import std.string; 4 import std.conv:to; 5 import std.json; 6 import std.file; 7 import std.path; 8 9 10 struct Cache 11 { 12 DirEntry entry; 13 bool valid; 14 } 15 struct AssetCache 16 { 17 string timeModified; 18 string timeCopied; 19 } 20 21 string getCachePath(string workingDir, string from, string toWhere) 22 { 23 string temp = buildNormalizedPath(workingDir, from, toWhere); 24 temp = to!string(hashOf(temp)); 25 return ".cache/"~temp~".json"; 26 } 27 bool cacheExists(string workingDir, string from, string toWhere) 28 { 29 return exists(getCachePath(workingDir,from,toWhere)); 30 } 31 32 string getCacheName(string fileName) 33 { 34 char[] ret = fileName.dup; 35 for(int i= 0; i < ret.length;i++) 36 { 37 if(fileName[i] == '\\') 38 ret[i] = '/'; 39 } 40 return cast(string)ret; 41 } 42 43 bool isCacheValid = true; 44 45 AssetCache[string] cache; 46 void readCacheFile(string workingDir, string from, string toWhere) 47 { 48 if(!cacheExists(workingDir, from, toWhere)) 49 return; 50 JSONValue json = parseJSON(readText(getCachePath(workingDir, from, toWhere))); 51 52 foreach (string k, JSONValue v; json.object) 53 { 54 JSONValue arr = v.array; 55 cache[k] = AssetCache(arr[0].str, arr[1].str); 56 } 57 } 58 59 void writeCacheFile(string workingDir, string from, string toWhere) 60 { 61 string toWrite = "{"; 62 foreach(k, v; cache) 63 { 64 toWrite~= "\""~k~"\": [\""~ v.timeModified~"\", \""~v.timeCopied~"\"],"; 65 } 66 if(!exists(".cache")) 67 mkdir(".cache"); 68 69 std.file.write(getCachePath(workingDir,from,toWhere), toWrite[0..$-1]~"}"); 70 } 71 72 bool isUpToDate(DirEntry which) 73 { 74 string* time = cast(string*)(which.name in cache); 75 if(time is null) 76 return false; 77 return *time == which.timeLastModified().toSimpleString(); 78 } 79 80 81 /** 82 * Receives from and where 83 */ 84 void copyResources(ref Terminal t, string inputPath, string outputPath, bool clean) 85 { 86 string workingDir = getcwd(); 87 //Populate cache with current content 88 readCacheFile(workingDir, inputPath, outputPath); 89 if(clean) 90 { 91 t.writeln("Cleaning target path: ", outputPath); 92 if(exists(outputPath)) 93 rmdirRecurse(outputPath); 94 } 95 if(!exists(outputPath)) 96 mkdirRecurse(outputPath); 97 98 //Copy loop 99 uint copyCount = 0; 100 foreach(DirEntry e; dirEntries(inputPath, SpanMode.depth)) 101 { 102 if(e.isDir) 103 continue; 104 Cache c = Cache(e, isUpToDate(e)); 105 //Check cache validity 106 //Make path relative to where it is started, so it is possible to create same folder structure 107 string relative = relativePath(c.entry.name, inputPath); 108 109 //Join target path with the source name 110 string name = buildNormalizedPath(outputPath, relative); 111 112 bool willCopy = false; 113 114 auto cacheName = getCacheName(relative); 115 AssetCache* temp = (cacheName in cache); 116 if(temp is null) 117 { 118 cache[cacheName] = AssetCache(c.entry.timeLastModified.toSimpleString, ""); 119 temp = (cacheName in cache); 120 } 121 else 122 { 123 willCopy = temp.timeModified != c.entry.timeLastModified.toSimpleString; 124 temp.timeModified = c.entry.timeLastModified.toSimpleString; 125 } 126 127 //If the target exists, check its time copied, if it is the same on the cache, willCopy = false 128 if(exists(name)) 129 { 130 if(!willCopy) 131 willCopy = (temp.timeCopied == "" || temp.timeCopied != e.timeLastModified.toSimpleString); 132 } 133 //Check if the path where the name is exists, for creating a folder for it 134 else 135 { 136 long ind = lastIndexOf(name, dirSeparator); 137 string namePath = name[0..ind]; 138 if(!exists(namePath)) 139 mkdirRecurse(namePath); 140 willCopy = true; 141 } 142 143 if(willCopy) 144 { 145 copyCount++; 146 t.writeln(c.entry.name, " -> ", name); 147 copy(c.entry.name, name); 148 cache[cacheName].timeCopied = DirEntry(name).timeLastModified.toSimpleString; 149 } 150 } 151 if(copyCount != 0) 152 { 153 writeCacheFile(workingDir, inputPath, outputPath); 154 t.writeln("CopyResources: Copied ", copyCount, " files"); 155 } 156 else 157 { 158 t.writeln("CopyResources: Target folder '"~outputPath~"' is up to date"); 159 } 160 }